//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <stdlib.h>
#include <elasys_server.h>
#include <elapi.h>
#include <_pubcrt.h>
#include "CFileOutputStream.h"
#include "_CFileOutputStream.cpp"

CFileOutputStream::~CFileOutputStream()
{
    Close();
}

ECode CFileOutputStream::Close()
{
    if (m_pIab) {
        m_pIab->Release();
        m_pIab = NULL;
    }

    return NOERROR;
}

ECode CFileOutputStream::Flush()
{
    if (!m_pIab) {
        return E_CLOSED_STREAM;
    }

    IBasicFile * pBasicFile = IBasicFile::Probe(m_pIab);
    if (!pBasicFile) {
        return E_NO_INTERFACE;
    }

    return pBasicFile->Sync();
}

ECode CFileOutputStream::Write(
    /* [in] */ Byte byte)
{
    if (!m_pIab) {
        return E_CLOSED_STREAM;
    }

    BufferOf<Byte> tmp(&byte, 1);
    Int32 nWritten;
    return WriteBufferEx(0, 1, tmp, &nWritten);
}

ECode CFileOutputStream::WriteBuffer(
    /* [in] */ const BufferOf<Byte> & buffer,
    /* [out] */ Int32 * pBytesWritten)
{
    if (buffer.IsNullOrEmpty() || !pBytesWritten) {
        return E_INVALID_ARGUMENT;
    }

    if (!m_pIab) {
        return E_CLOSED_STREAM;
    }

    return WriteBufferEx(0, buffer.GetUsed(), buffer, pBytesWritten);
}

/**
  * Writes <code>len</code> bytes from the specified byte array
  * starting at offset <code>offset</code> to this file output stream.
  */
ECode CFileOutputStream::WriteBufferEx(
    /* [in] */ Int32 offset,
    /* [in] */ Int32 length,
    /* [in] */ const BufferOf<Byte> & buffer,
    /* [out] */ Int32 * pBytesWritten)
{
    if (offset < 0 || length <= 0 || offset+length > buffer.GetUsed() || !pBytesWritten) {
        return E_INVALID_ARGUMENT;
    }

    if (!m_pIab) {
        return E_CLOSED_STREAM;
    }

    MemoryBuf tmp(buffer.GetPayload() + offset, length);

    ECode ec = m_pIab->Write(m_pos, tmp, pBytesWritten);
    if (SUCCEEDED(ec)) {
        m_pos += *pBytesWritten;
    }

    return ec;
}

ECode CFileOutputStream::GetFileInterface(
    /* [out] */ IBasicFile ** ppFile)
{
    if (!ppFile) {
        return E_INVALID_ARGUMENT;
    }

    if (!m_pIab) {
        return E_CLOSED_STREAM;
    }

    *ppFile = IBasicFile::Probe(m_pIab);
    if (!(*ppFile)) {
        return E_NO_INTERFACE;
    }

    (*ppFile)->AddRef();
    return NOERROR;
}

ECode CFileOutputStream::constructor(
    /* [in] */ AString fileName,
    /* [in] */ Boolean append)
{
    if (fileName.IsNullOrEmpty())
        return E_INVALID_ARGUMENT;

    WStringBuf_<_MAX_PATH> wfileName;
    wfileName.Append(fileName);

    return constructor(wfileName, append);
}

ECode CFileOutputStream::constructor(
    /* [in] */ WString fileName,
    /* [in] */ Boolean append)
{
    if (fileName.IsNullOrEmpty())
        return E_INVALID_ARGUMENT;

    IDiskManager * pDiskMgr = NULL;
    IBasicDirectory * pDir = NULL;
    IBasicFile * pBasicFile = NULL;

    wchar_t wszFullPath[_MAX_PATH];

    if (FAILED(WFullPath(wszFullPath, fileName, _MAX_PATH))) {
        return E_INVALID_ARGUMENT;
    }

    ECode ec = _CSystem_FindRunningObject(L"diskmgr", (IInterface **)&pDiskMgr);
    if (FAILED(ec)) {
        return ec;
    }

    WStringBuf_<_MAX_FNAME> wsbFileName;

    ec = pDiskMgr->AcquireDirInterface(wszFullPath, &pDir, &wsbFileName);
    if (FAILED(ec)) {
        goto Exit;
    }

    if (wsbFileName.GetLength()) {
        ec = pDir->Lookup(wsbFileName, &pBasicFile);
        if (FAILED(ec)) {
            if (ec == E_FS_NO_SUCH_FILE || ec == E_PATH_NOT_FOUND) {
                ec = pDir->CreateFile(wsbFileName, FileAccessMode_ReadWrite,
                                      FALSE, &pBasicFile);
            }

            if (FAILED(ec)) {
                goto Exit;
            }
        }
        m_pIab = IAbsoluteIO::Probe((IAbsoluteIO *)pBasicFile);
        if (!m_pIab) {
            ec = E_NO_INTERFACE;
            goto Exit;
        }
        m_pIab->AddRef();

        if (append) {
            ec = m_pIab->GetSize(&m_pos);
        } else {
            ec = m_pIab->SetSize(0);
        }

        if (FAILED(ec))
            goto Exit;
    } else {
        ec = E_INVALID_ARGUMENT;
    }

Exit:
    if (pDiskMgr) {
        pDiskMgr->Release();
    }

    if (pDir) {
        pDir->Release();
    }

    if (pBasicFile) {
        pBasicFile->Release();
    }

    return ec;
}

ECode CFileOutputStream::constructor(
    /* [in] */ IBasicFile * pFile,
    /* [in] */ Boolean append)
{
    m_pIab = IAbsoluteIO::Probe(pFile);
    if (!m_pIab) {
        return E_NO_INTERFACE;
    }
    m_pIab->AddRef();

    ECode ec;
    if (append) {
        ec = m_pIab->GetSize(&m_pos);
    } else {
        ec = m_pIab->SetSize(0);
    }

    return ec;
}
